﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Collections.Generic;

using SLARToolKit;
using EasyAR;

namespace EasyAR.Model
{
    public class ARSilverlightModel
    {
        ARFactory _arFactory;
        public CaptureSourceMarkerDetector ArDetector { get; set; }
        Matrix3D projectionMatrix;
        public Panel ViewportContainer { get; set; }
        public double Rotate { get; set; }
        public double Scale { get; set; }

        public ARSilverlightModel(ARFactory arFactory)
        {
            _arFactory = arFactory;
        }

        public void InitializeDetector(CaptureSource captureSource,double nearPlane, double farPlane)
        {
            Scale = 0.4;
            Rotate = 0;
            ArDetector = new CaptureSourceMarkerDetector(captureSource, nearPlane, farPlane, _arFactory.MarkerList.ToList());
        }

        public void StartARSilverlight()
        {
            AttachAREvent();
        }

        public delegate void BeforeItemDetectedEventHandler(FrameworkElement element);
        public event BeforeItemDetectedEventHandler BeforeItemDetected;

        public delegate void AfterItemDetectedEventHandler(FrameworkElement element);
        public event AfterItemDetectedEventHandler AfterItemDetected;

        private void AttachAREvent()
        {
            ArDetector.MarkersDetected += new EventHandler<MarkerDetectionEventArgs>((s, e) =>
            {
                var detectedResults = e.DetectionResults;
                projectionMatrix = ArDetector.Projection;
                ViewportContainer.Dispatcher.BeginInvoke(() =>
                {
                    foreach (var element in _arFactory.ARItems.Values)
                    {
                        BeforeItemDetected(element);
                    }
                    if (detectedResults.HasResults)
                    {
                        ApplyTransformations(detectedResults);
                    }
                });
            });
        }

        private void ApplyTransformations(DetectionResults detectedResults)
        {
            var resultMatch = from result in detectedResults
                              join arItem in _arFactory.ARItems
                              on result.Marker equals arItem.Key
                              select new { Element = arItem.Value, Transformer = result.Transformation };
            foreach (var item in resultMatch)
            {
                AfterItemDetected(item.Element);
                ApplyTransformation(item.Element, item.Transformer);
            }
            
        }

        private void ApplyTransformation(FrameworkElement element, Matrix3D baseTransformation)
        {
            // Create additional transformations
            var centerImageAtOrigin = Matrix3DFactory.CreateTranslation(-element.ActualWidth * 0.5, -element.ActualHeight * 0.5, 0);
            var invertYAxis = Matrix3DFactory.CreateScale(1, -1, 1);
            var viewport = Matrix3DFactory.CreateViewportTransformation(ViewportContainer.ActualWidth, ViewportContainer.ActualHeight);
            var rotate = Matrix3DFactory.CreateRotationZ(MathHelper.ToRadians(Rotate));
            var scale = Matrix3DFactory.CreateScale(Scale);

            // Compose transform
            var m = Matrix3D.Identity;
            m = m * centerImageAtOrigin;
            m = m * invertYAxis;
            m = m * rotate;
            m = m * scale;
            m = m * baseTransformation;
            m = Matrix3DFactory.CreateViewportProjection(m, Matrix3D.Identity, projectionMatrix, viewport);

            // Apply transform
            element.Projection = new Matrix3DProjection { ProjectionMatrix = m };
        }
    }
}